/* Copyright 2012 Tobias Marschall
 *
 * This file is part of CLEVER.
 *
 * CLEVER is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * CLEVER is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with CLEVER.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "LengthAwareVariationIndex.h"

using namespace std;

const int LengthAwareVariationIndex::INDEX_COUNT = 28;

LengthAwareVariationIndex::LengthAwareVariationIndex(const std::vector<Variation>& variations) : variations(variations), indexes(), variation_index_map(2*INDEX_COUNT+1, vector<size_t>()) {
	vector<vector<Variation> > stratified_variations(2*INDEX_COUNT+1, vector<Variation>());
	for (size_t i=0; i<variations.size(); ++i) {
		size_t j = getIndexByLength(variations[i].getLengthDifference());
		assert(j < stratified_variations.size());
		stratified_variations[j].push_back(variations[i]);
		variation_index_map[j].push_back(i);
	}
	for (size_t i=0; i<stratified_variations.size(); ++i) {
		indexes.push_back(new VariationIndex(stratified_variations[i]));
	}
}

LengthAwareVariationIndex::~LengthAwareVariationIndex() {
	for (size_t i=0; i<indexes.size(); ++i) {
		delete indexes[i];
	}
}

size_t LengthAwareVariationIndex::getIndexByLength(int length) {
	if (abs(length) < 16) {
		return INDEX_COUNT;
	}
	int offset = min((int)log2(abs(length))-3, INDEX_COUNT);
	if (length<0) {
		return INDEX_COUNT - offset;
	} else {
		return INDEX_COUNT + offset;
	}
}

auto_ptr<vector<size_t> > LengthAwareVariationIndex::containedIn(const std::string& chromosome, size_t start, size_t end, int min_length, int max_length) {
	assert(min_length <= max_length);
	size_t first_index = this->getIndexByLength(min_length);
	size_t last_index = this->getIndexByLength(max_length);
	assert(first_index<=last_index);
	auto_ptr<vector<size_t> > result(0);
	for (size_t i=first_index; i<=last_index; ++i) {
		auto_ptr<vector<size_t> > sub_result = indexes[i]->containedIn(chromosome, start, end);
		if (sub_result.get() != 0) {
			for (vector<size_t>::const_iterator it = sub_result->begin(); it != sub_result->end(); ++it) {
				size_t variation_id = variation_index_map[i][*it];
				int length = variations[variation_id].getLengthDifference();
				if ((length >= min_length) && (length <= max_length)) {
					if (result.get() == 0) {
						result = auto_ptr<vector<size_t> >(new vector<size_t>());
					}
					result->push_back(variation_id);
				}
			}
		}
	}
	return result;
}


